let Vue;

const forEachValue = ((obj,callback) => {
    Object.keys(obj).forEach(key=>{
        callback(obj[key],key);
    })
});

class Store{
    constructor(options){
        let state = options.state; // 直接将用户的定义的状态放在容器的store里
        let computed = {};

        // 获取到所有的getter属性
        let getters = options.getters;
        this.getters = {}
        forEachValue(getters,(fn,key)=>{
            computed[key] = ()=>{ // 将用户的实现保存在computed上
                return fn(this.state);
            }
            Object.defineProperty(this.getters,key,{ // 取值获取计算属性
                get:()=>{
                    return this.vm[key]; // 如果依赖的值不变，走缓存
                }
            })
        })

        this.vm = new Vue({
            data:{
                $$state:state // 定义$开头的属性 不会定义在vm上
            },
            computed // 将用户的属性存在computed中
        })


        // 更改数据中值
        let actions = options.actions;
        let mutations = options.mutations;
        this.actions = {};
        this.mutations = {};

        forEachValue(mutations,(fn,key)=>{ // 订阅模式
            this.mutations[key] = (payload) => fn(this.state,payload);
            // store.commit('changeAge',100)
        })

        forEachValue(actions,(fn,key)=>{ // 订阅模式
            this.actions[key] = (payload) => fn(this,payload);
            // store.dispatch('changeAge',100)
        })


        console.log(`new store`);
    }

    commit = (type,payload) =>{ // this永远指向store容器
        this.mutations[type](payload);
    }

    dispatch = (type,payload) =>{ // this永远指向store容器
        this.actions[type](payload);
    }

    get state(){
        return this.vm._data.$$state;
    }
}

const Vuex = {
  Store,
  install(_Vue){
    Vue = _Vue;
    // 需要获取到父组件的store属性，每个组件运行的时候都能拿到store
    Vue.mixin({
        // 混入创建之初
        beforeCreate(){
            const options = this.$options;
            if(options.store){ // 根组件
                this.$store = options.store; // 根组件上有一个$store 属性
            }else{ // 子组件、平级组件(排除平级组件)
                // 判断非根组件，获取组件的$store
                if(this.$parent && this.$parent.$store){
                    this.$store = this.$parent.$store;
                }
            }
        }
    })
  }
}

export default Vuex;